package com.idea.relax.tool.core;

import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Stream;

/**
 * @className: StringUtil
 * @description:
 * @author: salad
 * @date: 2022/2/25
 **/
public class StringUtil extends org.springframework.util.StringUtils {


	public static boolean isBlank(final CharSequence cs) {
		return !hasText(cs);
	}

	public static boolean isNotBlank(final CharSequence cs) {
		return hasText(cs);
	}

	/**
	 * 有 任意 一个 Blank
	 *
	 * @param css CharSequence
	 * @return boolean
	 */
	public static boolean isAnyBlank(final CharSequence... css) {
		if (ObjectUtils.isEmpty(css)) {
			return true;
		}
		return Stream.of(css).anyMatch(StringUtil::isBlank);
	}


	/**
	 * 是否全非 Blank
	 *
	 * @param css CharSequence
	 * @return boolean
	 */
	public static boolean isNoneBlank(final CharSequence... css) {
		if (ObjectUtils.isEmpty(css)) {
			return false;
		}
		return Stream.of(css).allMatch(StringUtil::isNotBlank);
	}

	/**
	 * 判断一个字符串是否是数字
	 *
	 * @param cs the CharSequence to check, may be null
	 * @return {boolean}
	 */
	public static boolean isNumber(final CharSequence cs) {
		if (isBlank(cs)) {
			return false;
		}
		for (int i = cs.length(); --i >= 0; ) {
			int chr = cs.charAt(i);
			//ascii码小于48或者大于57的就不是一个数字
			if (chr < 48 || chr > 57) {
				return false;
			}
		}
		return true;
	}

	public static boolean eq(final CharSequence v1, final CharSequence v2) {
		return eq(v1, v2, false);
	}

	public static boolean eqIgnoreCase(final CharSequence v1, final CharSequence v2) {
		return eq(v1, v2, true);
	}

	public static boolean eq(CharSequence str1, CharSequence str2, boolean ignoreCase) {
		if (null == str1) {
			return str2 == null;
		} else if (null == str2) {
			return false;
		} else {
			return ignoreCase ? str1.toString().equalsIgnoreCase(str2.toString()) : str1.equals(str2);
		}
	}

	/**
	 * 驼峰命名转下划线分割命名
	 */
	public static String humpToUnderline(@Nullable String para) {
		para = firstCharToLower(para);
		StringBuilder sb = new StringBuilder(para);
		int temp = 0;

		for (int i = 0; i < para.length(); ++i) {
			if (Character.isUpperCase(para.charAt(i))) {
				sb.insert(i + temp, "_");
				++temp;
			}
		}
		return sb.toString().toLowerCase();
	}


	public static String firstCharToLower(String str) {
		char firstChar = str.charAt(0);
		if (firstChar >= 'A' && firstChar <= 'Z') {
			char[] arr = str.toCharArray();
			arr[0] = (char) (arr[0] + 32);
			return new String(arr);
		} else {
			return str;
		}
	}

	public static String removeSuffix(CharSequence str, CharSequence suffix) {
		if (!Func.isEmpty(str) && !Func.isEmpty(suffix)) {
			String str2 = str.toString();
			return str2.endsWith(suffix.toString()) ? subPre(str2, str2.length() - suffix.length()) : str2;
		} else {
			return "";
		}
	}

	public static String subPre(CharSequence string, int toIndex) {
		return sub(string, 0, toIndex);
	}


	public static StringBuilder appendBuilder(StringBuilder sb, CharSequence... strs) {
		for (CharSequence str : strs) {
			sb.append(str);
		}
		return sb;
	}

	public static String join(Collection<?> coll, String delimiter,
							  String prefix, String suffix,
							  String emptyValue, boolean skipEmpty) {
		if (Func.isEmpty(coll)) {
			return emptyValue;
		}

		Iterator<?> iterator = coll.iterator();
		StringJoiner joiner = new StringJoiner(delimiter, prefix, suffix);

		if (null != emptyValue) {
			joiner.setEmptyValue(emptyValue);
		}

		while (iterator.hasNext()) {
			Object next = iterator.next();
			if (skipEmpty) {
				if (null != next && isNotBlank(String.valueOf(next))) {
					joiner.add(String.valueOf(next));
				}
			} else {
				joiner.add(String.valueOf(next));
			}
		}
		return joiner.toString();
	}

	public static String join(Collection<?> coll, String delimiter, boolean isSkipEmpty) {
		return join(coll, delimiter, "", "", "", isSkipEmpty);
	}

	public static String join(Collection<?> coll, String delimiter) {
		return join(coll, delimiter, false);
	}

	public static String join(Collection<?> coll) {
		return join(coll, ",");
	}

	@SafeVarargs
	public static <T> String join(T... array) {
		return join(",", array);
	}

	@SafeVarargs
	public static <T> String join(String delimiter, T... array) {
		return join(Arrays.asList(array), delimiter);
	}


	public static String getColumn(String str, String keyword) {
		return humpToUnderline(removeSuffix(str, keyword));
	}


	/**
	 * 同 log 格式的 format 规则
	 * <p>
	 *
	 * @param message   需要转换的字符串
	 * @param arguments 需要替换的变量
	 * @return 转换后的字符串
	 */
	public static String format(@Nullable String message, @Nullable Object... arguments) {
		// message 为 null 返回空字符串
		if (message == null) {
			return StringPool.EMPTY;
		}
		// 参数为 null 或者为空
		if (arguments == null || arguments.length == 0) {
			return message;
		}
		StringBuilder sb = new StringBuilder((int) (message.length() * 1.5));
		int cursor = 0;
		int index = 0;
		int argsLength = arguments.length;
		for (int start, end; (start = message.indexOf(StringPool.LEFT_BRACE, cursor)) != -1 && (end = message.indexOf(StringPool.RIGHT_BRACE, start)) != -1 && index < argsLength; ) {
			sb.append(message, cursor, start);
			sb.append(arguments[index]);
			cursor = end + 1;
			index++;
		}
		sb.append(message.substring(cursor));
		return sb.toString();
	}

	/**
	 * 生成uuid
	 *
	 * @return UUID
	 */
	public static String randomUUID() {
		ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
		return new UUID(threadLocalRandom.nextLong(), threadLocalRandom.nextLong()).toString().replaceAll("-", "");
	}

	/**
	 * 清理字符串，清理出某些不可见字符
	 *
	 * @param target 字符串
	 * @return {String}
	 */
	public static String cleanCuriousChars(String target) {
		return target.replaceAll("[ 　`·•�\\f\\t\\v\\s]", "");
	}

	/**
	 * 获取标识符，用于参数清理
	 *
	 * @param param 参数
	 * @return 清理后的标识符
	 */
	@Nullable
	public static String cleanIdentifier(@Nullable String param) {
		if (param == null) {
			return null;
		}
		StringBuilder paramBuilder = new StringBuilder();
		for (int i = 0; i < param.length(); i++) {
			char c = param.charAt(i);
			if (Character.isJavaIdentifierPart(c)) {
				paramBuilder.append(c);
			}
		}
		return paramBuilder.toString();
	}


	/**
	 * 改进JDK subString<br>
	 * index从0开始计算，最后一个字符为-1<br>
	 * 如果from和to位置一样，返回 "" <br>
	 * 如果from或to为负数，则按照length从后向前数位置，如果绝对值大于字符串长度，则from归到0，to归到length<br>
	 * 如果经过修正的index中from大于to，则互换from和to example: <br>
	 * abcdefgh 2 3 =》 c <br>
	 * abcdefgh 2 -3 =》 cde <br>
	 *
	 * @param str       String
	 * @param fromIndex 开始的index（包括）
	 * @param toIndex   结束的index（不包括）
	 * @return 字串
	 */
	public static String sub(CharSequence str, int fromIndex, int toIndex) {
		if (isEmpty(str)) {
			return StringPool.EMPTY;
		}
		int len = str.length();

		if (fromIndex < 0) {
			fromIndex = len + fromIndex;
			if (fromIndex < 0) {
				fromIndex = 0;
			}
		} else if (fromIndex > len) {
			fromIndex = len;
		}

		if (toIndex < 0) {
			toIndex = len + toIndex;
			if (toIndex < 0) {
				toIndex = len;
			}
		} else if (toIndex > len) {
			toIndex = len;
		}

		if (toIndex < fromIndex) {
			int tmp = fromIndex;
			fromIndex = toIndex;
			toIndex = tmp;
		}

		if (fromIndex == toIndex) {
			return StringPool.EMPTY;
		}

		return str.toString().substring(fromIndex, toIndex);
	}

	/**
	 * 首字母变小写
	 *
	 * @param str 字符串
	 * @return {String}
	 */
	public static String lowerFirst(String str) {
		char firstChar = str.charAt(0);
		if (firstChar >= StringPool.U_A && firstChar <= StringPool.U_Z) {
			char[] arr = str.toCharArray();
			arr[0] += (StringPool.L_A - StringPool.U_A);
			return new String(arr);
		}
		return str;
	}

	/**
	 * 首字母变大写
	 *
	 * @param str 字符串
	 * @return {String}
	 */
	public static String upperFirst(String str) {
		char firstChar = str.charAt(0);
		if (firstChar >= StringPool.L_A && firstChar <= StringPool.L_Z) {
			char[] arr = str.toCharArray();
			arr[0] -= (StringPool.L_A - StringPool.U_A);
			return new String(arr);
		}
		return str;
	}

	/**
	 * 指定范围内查找指定字符
	 *
	 * @param str        字符串
	 * @param searchChar 被查找的字符
	 * @return 位置
	 */
	public static int indexOf(final CharSequence str, char searchChar) {
		return indexOf(str, searchChar, 0);
	}

	/**
	 * 指定范围内查找指定字符
	 *
	 * @param str        字符串
	 * @param searchChar 被查找的字符
	 * @param start      起始位置，如果小于0，从0开始查找
	 * @return 位置
	 */
	public static int indexOf(final CharSequence str, char searchChar, int start) {
		if (str instanceof String) {
			return ((String) str).indexOf(searchChar, start);
		} else {
			return indexOf(str, searchChar, start, -1);
		}
	}

	/**
	 * 指定范围内查找指定字符
	 *
	 * @param str        字符串
	 * @param searchChar 被查找的字符
	 * @param start      起始位置，如果小于0，从0开始查找
	 * @param end        终止位置，如果超过str.length()则默认查找到字符串末尾
	 * @return 位置
	 */
	public static int indexOf(final CharSequence str, char searchChar, int start, int end) {
		final int len = str.length();
		if (start < 0 || start > len) {
			start = 0;
		}
		if (end > len || end < 0) {
			end = len;
		}
		for (int i = start; i < end; i++) {
			if (str.charAt(i) == searchChar) {
				return i;
			}
		}
		return -1;
	}

	/**
	 * 统计指定内容中包含指定字符串的数量<br>
	 * 参数为 {@code null} 或者 "" 返回 {@code 0}.
	 *
	 * <pre>
	 * StringUtil.count(null, *)       = 0
	 * StringUtil.count("", *)         = 0
	 * StringUtil.count("abba", null)  = 0
	 * StringUtil.count("abba", "")    = 0
	 * StringUtil.count("abba", "a")   = 2
	 * StringUtil.count("abba", "ab")  = 1
	 * StringUtil.count("abba", "xxx") = 0
	 * </pre>
	 *
	 * @param content      被查找的字符串
	 * @param strForSearch 需要查找的字符串
	 * @return 查找到的个数
	 */
	public static int count(CharSequence content, CharSequence strForSearch) {
		if (isAnyBlank(content, strForSearch) || strForSearch.length() > content.length()) {
			return 0;
		}

		int count = 0;
		int idx = 0;
		final String content2 = content.toString();
		final String strForSearch2 = strForSearch.toString();
		while ((idx = content2.indexOf(strForSearch2, idx)) > -1) {
			count++;
			idx += strForSearch.length();
		}
		return count;
	}

	/**
	 * 统计指定内容中包含指定字符的数量
	 *
	 * @param content       内容
	 * @param charForSearch 被统计的字符
	 * @return 包含数量
	 */
	public static int count(CharSequence content, char charForSearch) {
		int count = 0;
		if (isBlank(content)) {
			return 0;
		}
		int contentLength = content.length();
		for (int i = 0; i < contentLength; i++) {
			if (charForSearch == content.charAt(i)) {
				count++;
			}
		}
		return count;
	}

	/**
	 * 下划线转驼峰
	 *
	 * @param para 字符串
	 * @return String
	 */
	public static String underlineToHump(String para) {
		StringBuilder result = new StringBuilder();
		String[] a = para.split("_");
		for (String s : a) {
			if (result.length() == 0) {
				result.append(s.toLowerCase());
			} else {
				result.append(s.substring(0, 1).toUpperCase());
				result.append(s.substring(1).toLowerCase());
			}
		}
		return result.toString();
	}

	/**
	 * 驼峰转横线
	 *
	 * @param para 字符串
	 * @return String
	 */
	public static String humpToLine(String para) {
		para = lowerFirst(para);
		StringBuilder sb = new StringBuilder(para);
		int temp = 0;
		for (int i = 0; i < para.length(); i++) {
			if (Character.isUpperCase(para.charAt(i))) {
				sb.insert(i + temp, "-");
				temp += 1;
			}
		}
		return sb.toString().toLowerCase();
	}

	/**
	 * 随机数生成
	 *
	 * @param count 字符长度
	 * @return 随机数
	 */
	public static String random(int count) {
		if (count == 0) {
			return "";
		}
		Assert.isTrue(count > 0, "Requested random string length " + count + " is less than 0.");
		final ThreadLocalRandom random = ThreadLocalRandom.current();
		char[] buffer = new char[count];
		for (int i = 0; i < count; i++) {
			buffer[i] = StringPool.ENGLISH_WORDS.charAt(random.nextInt(StringPool.ENGLISH_WORDS.length()));
		}
		return new String(buffer);
	}

}
